#include "pointlightsource.h"
#include "matrix.h"
#include <cmath>
#include <GL/gl.h>


PointLightSource::PointLightSource(const Point &location)
    : location_(location),
    constant_distance_factor_(1.0),
    linear_distance_factor_(0.0),
    quadratic_distance_factor_(0.0)
{
}

LightIntensity PointLightSource::intensity(const Point &p) const
{
    LightIntensity it;
    Vector I = location_ - p;
    double distance = I.norm();
    const double denominator = constant_distance_factor_ +
                               linear_distance_factor_*distance +
                               quadratic_distance_factor_*distance;
    const double distance_atenuation = 1.0 / denominator;

    it.set_ambient(this->ambient());
    it.set_diffuse(distance_atenuation * this->diffuse());
    it.set_specular(distance_atenuation * this->specular());

    return it;
}

void PointLightSource::applyCameraTransformation(const Camera &camera)
{
    location_ = Matrix::coordChangeMatrix(camera.frame()) * location_;
}

void PointLightSource::glSetup(int light_id) const
{
    GLfloat light_pos[] = {location_(0), location_(1), location_(2), 1.0};
    GLfloat gl_ambient[] = {ambient().r(), ambient().g(), ambient().b(), 1.0};
    GLfloat gl_diffuse[] = {diffuse().r(), diffuse().g(), diffuse().b(), 1.0};
    GLfloat gl_specular[] = {specular().r(), specular().g(), specular().b(), 1.0};

    glLightfv(light_id, GL_POSITION, light_pos);
    glLightfv(light_id, GL_AMBIENT, gl_ambient);
    glLightfv(light_id, GL_DIFFUSE, gl_diffuse);
    glLightfv(light_id, GL_SPECULAR, gl_specular);
    glLightf(light_id, GL_CONSTANT_ATTENUATION, constant_distance_factor_);
    glLightf(light_id, GL_LINEAR_ATTENUATION, linear_distance_factor_);
    glLightf(light_id, GL_QUADRATIC_ATTENUATION, quadratic_distance_factor_);
}

Vector PointLightSource::direction(const Point &p) const
{
    Vector d = location_ - p;
    return d.normalize();
}

ILightSource* PointLightSource::clone() const
{
    PointLightSource *light = new PointLightSource(location_);

    light->intensities_ = this->intensities_;
    return light;
}
